

<!DOCTYPE html>
<html lang="zh-CN" data-default-color-scheme=auto>



<head>
  <meta name="referrer" content="no-referrer" />
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/img/apple-touch-icon.png">
  <link rel="icon" href="/img/fyy.png">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="author" content="fyy">
  <meta name="keywords" content="">
  
    <meta name="description" content="数据库命名规范 所有数据库对象名称必须使用小写字母并用下划线分割 所有数据库对象名称禁止使用 MySQL 保留关键字（如果表名中包含关键字查询时，需要将其用单引号括起来） 数据库对象的命名要能做到见名识意，并且最后不要超过 32 个字符 临时库表必须以 tmp_ 为前缀并以日期为后缀，备份表必须以 bak_ 为前缀并以日期 (时间戳) 为后缀 所有存储相同数据的列名和列类型必须一致（一般作为关联列">
<meta property="og:type" content="article">
<meta property="og:title" content="MySQL高性能优化规范建议总结">
<meta property="og:url" content="http://example.com/2021/04/06/DB/MySQL/11.%20MySQL%E9%AB%98%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E8%A7%84%E8%8C%83%E5%BB%BA%E8%AE%AE%E6%80%BB%E7%BB%93/index.html">
<meta property="og:site_name" content="fyy-coding">
<meta property="og:description" content="数据库命名规范 所有数据库对象名称必须使用小写字母并用下划线分割 所有数据库对象名称禁止使用 MySQL 保留关键字（如果表名中包含关键字查询时，需要将其用单引号括起来） 数据库对象的命名要能做到见名识意，并且最后不要超过 32 个字符 临时库表必须以 tmp_ 为前缀并以日期为后缀，备份表必须以 bak_ 为前缀并以日期 (时间戳) 为后缀 所有存储相同数据的列名和列类型必须一致（一般作为关联列">
<meta property="og:locale" content="zh_CN">
<meta property="article:published_time" content="2021-04-06T04:13:33.000Z">
<meta property="article:modified_time" content="2024-03-18T12:13:44.085Z">
<meta property="article:author" content="fyy">
<meta property="article:tag" content="MySQL">
<meta name="twitter:card" content="summary_large_image">
  
  
  
  <title>MySQL高性能优化规范建议总结 - fyy-coding</title>

  <link  rel="stylesheet" href="https://lib.baomitu.com/twitter-bootstrap/4.6.1/css/bootstrap.min.css" />



  <link  rel="stylesheet" href="https://lib.baomitu.com/github-markdown-css/4.0.0/github-markdown.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/hint.css/2.7.0/hint.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.css" />



<!-- 主题依赖的图标库，不要自行修改 -->
<!-- Do not modify the link that theme dependent icons -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_hj8rtnfg7um.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_lbnruvf0jn.css">


<link  rel="stylesheet" href="/css/main.css" />


  <link id="highlight-css" rel="stylesheet" href="/css/highlight.css" />
  
    <link id="highlight-css-dark" rel="stylesheet" href="/css/highlight-dark.css" />
  




  <script id="fluid-configs">
    var Fluid = window.Fluid || {};
    Fluid.ctx = Object.assign({}, Fluid.ctx)
    var CONFIG = {"hostname":"example.com","root":"/","version":"1.9.7","typing":{"enable":true,"typeSpeed":70,"cursorChar":"_","loop":false,"scope":[]},"anchorjs":{"enable":true,"element":"h1,h2,h3,h4,h5,h6","placement":"left","visible":"hover","icon":""},"progressbar":{"enable":true,"height_px":3,"color":"#29d","options":{"showSpinner":false,"trickleSpeed":100}},"code_language":{"enable":true,"default":"TEXT"},"copy_btn":true,"image_caption":{"enable":true},"image_zoom":{"enable":true,"img_url_replace":["",""]},"toc":{"enable":true,"placement":"right","headingSelector":"h1,h2,h3,h4,h5,h6","collapseDepth":0},"lazyload":{"enable":true,"loading_img":"/img/loading.gif","onlypost":false,"offset_factor":2},"web_analytics":{"enable":true,"follow_dnt":true,"baidu":null,"google":{"measurement_id":null},"tencent":{"sid":null,"cid":null},"woyaola":null,"cnzz":null,"leancloud":{"app_id":"ufph8TbmK43d1JfQxLvss4KY-MdYXbMMI","app_key":"96APiPOtZEiCQlv5h3jjZfbC","server_url":null,"path":"window.location.pathname","ignore_local":false}},"search_path":"/local-search.xml","include_content_in_search":true};

    if (CONFIG.web_analytics.follow_dnt) {
      var dntVal = navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack;
      Fluid.ctx.dnt = dntVal && (dntVal.startsWith('1') || dntVal.startsWith('yes') || dntVal.startsWith('on'));
    }
  </script>
  <script  src="/js/utils.js" ></script>
  <script  src="/js/color-schema.js" ></script>
  

  

  
    <!-- Google tag (gtag.js) -->
    <script async>
      if (!Fluid.ctx.dnt) {
        Fluid.utils.createScript("https://www.googletagmanager.com/gtag/js?id=", function() {
          window.dataLayer = window.dataLayer || [];
          function gtag() {
            dataLayer.push(arguments);
          }
          gtag('js', new Date());
          gtag('config', '');
        });
      }
    </script>
  

  

  

  

  
    
  



  
<meta name="generator" content="Hexo 6.3.0"></head>


<body>
  

  <header>
    

<div class="header-inner" style="height: 70vh;">
  <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand" href="/">
      <strong>fyy-coding</strong>
    </a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/" target="_self">
                <i class="iconfont icon-home-fill"></i>
                <span>首页</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/archives/" target="_self">
                <i class="iconfont icon-archive-fill"></i>
                <span>归档</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/categories/" target="_self">
                <i class="iconfont icon-category-fill"></i>
                <span>分类</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/tags/" target="_self">
                <i class="iconfont icon-tags-fill"></i>
                <span>标签</span>
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/about/" target="_self">
                <i class="iconfont icon-user-fill"></i>
                <span>关于</span>
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" target="_self" href="javascript:;" data-toggle="modal" data-target="#modalSearch" aria-label="Search">
              <i class="iconfont icon-search"></i>
            </a>
          </li>
          
        
        
          <li class="nav-item" id="color-toggle-btn">
            <a class="nav-link" target="_self" href="javascript:;" aria-label="Color Toggle">
              <i class="iconfont icon-dark" id="color-toggle-icon"></i>
            </a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

  

<div id="banner" class="banner" parallax=true
     style="background: url('/img/default.png') no-repeat center center; background-size: cover;">
  <div class="full-bg-img">
    <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
      <div class="banner-text text-center fade-in-up">
        <div class="h2">
          
            <span id="subtitle" data-typed-text="MySQL高性能优化规范建议总结"></span>
          
        </div>

        
          
  <div class="mt-3">
    
    
      <span class="post-meta">
        <i class="iconfont icon-date-fill" aria-hidden="true"></i>
        <time datetime="2021-04-06 12:13" pubdate>
          2021年4月6日 中午
        </time>
      </span>
    
  </div>

  <div class="mt-1">
    
      <span class="post-meta mr-2">
        <i class="iconfont icon-chart"></i>
        
          5.4k 字
        
      </span>
    

    
      <span class="post-meta mr-2">
        <i class="iconfont icon-clock-fill"></i>
        
        
        
          46 分钟
        
      </span>
    

    
    
      
        <span id="leancloud-page-views-container" class="post-meta" style="display: none">
          <i class="iconfont icon-eye" aria-hidden="true"></i>
          <span id="leancloud-page-views"></span> 次
        </span>
        
      
    
  </div>


        
      </div>

      
    </div>
  </div>
</div>

</div>

  </header>

  <main>
    
      

<div class="container-fluid nopadding-x">
  <div class="row nomargin-x">
    <div class="side-col d-none d-lg-block col-lg-2">
      

    </div>

    <div class="col-lg-8 nopadding-x-md">
      <div class="container nopadding-x-md" id="board-ctn">
        <div id="board">
          <article class="post-content mx-auto">
            <h1 id="seo-header">MySQL高性能优化规范建议总结</h1>
            
            
              <div class="markdown-body">
                
                <h2 id="数据库命名规范"><a href="#数据库命名规范" class="headerlink" title="数据库命名规范"></a>数据库命名规范</h2><ul>
<li>所有数据库对象名称必须使用小写字母并用下划线分割</li>
<li>所有数据库对象名称禁止使用 MySQL 保留关键字（如果表名中包含关键字查询时，需要将其用单引号括起来）</li>
<li>数据库对象的命名要能做到见名识意，并且最后不要超过 32 个字符</li>
<li>临时库表必须以 <code>tmp_</code> 为前缀并以日期为后缀，备份表必须以 <code>bak_</code> 为前缀并以日期 (时间戳) 为后缀</li>
<li>所有存储相同数据的列名和列类型必须一致（一般作为关联列，如果查询时关联列类型不一致会自动进行数据类型隐式转换，会造成列上的索引失效，导致查询效率降低）</li>
</ul>
<h2 id="数据库基本设计规范"><a href="#数据库基本设计规范" class="headerlink" title="#数据库基本设计规范"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E6%95%B0%E6%8D%AE%E5%BA%93%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83">#</a>数据库基本设计规范</h2><h3 id="所有表必须使用-InnoDB-存储引擎"><a href="#所有表必须使用-InnoDB-存储引擎" class="headerlink" title="#所有表必须使用 InnoDB 存储引擎"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E6%89%80%E6%9C%89%E8%A1%A8%E5%BF%85%E9%A1%BB%E4%BD%BF%E7%94%A8-innodb-%E5%AD%98%E5%82%A8%E5%BC%95%E6%93%8E">#</a>所有表必须使用 InnoDB 存储引擎</h3><p>没有特殊要求（即 InnoDB 无法满足的功能如：列存储，存储空间数据等）的情况下，所有表必须使用 InnoDB 存储引擎（MySQL5.5 之前默认使用 Myisam，5.6 以后默认的为 InnoDB）。</p>
<p>InnoDB 支持事务，支持行级锁，更好的恢复性，高并发下性能更好。</p>
<h3 id="数据库和表的字符集统一使用-UTF8"><a href="#数据库和表的字符集统一使用-UTF8" class="headerlink" title="#数据库和表的字符集统一使用 UTF8"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E6%95%B0%E6%8D%AE%E5%BA%93%E5%92%8C%E8%A1%A8%E7%9A%84%E5%AD%97%E7%AC%A6%E9%9B%86%E7%BB%9F%E4%B8%80%E4%BD%BF%E7%94%A8-utf8">#</a>数据库和表的字符集统一使用 UTF8</h3><p>兼容性更好，统一字符集可以避免由于字符集转换产生的乱码，不同的字符集进行比较前需要进行转换会造成索引失效，如果数据库中有存储 emoji 表情的需要，字符集需要采用 utf8mb4 字符集。</p>
<p>推荐阅读一下我写的这篇文章：<a target="_blank" rel="noopener" href="https://javaguide.cn/database/character-set.html">MySQL 字符集详解</a> 。</p>
<h3 id="所有表和字段都需要添加注释"><a href="#所有表和字段都需要添加注释" class="headerlink" title="#所有表和字段都需要添加注释"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E6%89%80%E6%9C%89%E8%A1%A8%E5%92%8C%E5%AD%97%E6%AE%B5%E9%83%BD%E9%9C%80%E8%A6%81%E6%B7%BB%E5%8A%A0%E6%B3%A8%E9%87%8A">#</a>所有表和字段都需要添加注释</h3><p>使用 comment 从句添加表和列的备注，从一开始就进行数据字典的维护</p>
<h3 id="尽量控制单表数据量的大小，建议控制在-500-万以内"><a href="#尽量控制单表数据量的大小，建议控制在-500-万以内" class="headerlink" title="#尽量控制单表数据量的大小，建议控制在 500 万以内"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E5%B0%BD%E9%87%8F%E6%8E%A7%E5%88%B6%E5%8D%95%E8%A1%A8%E6%95%B0%E6%8D%AE%E9%87%8F%E7%9A%84%E5%A4%A7%E5%B0%8F-%E5%BB%BA%E8%AE%AE%E6%8E%A7%E5%88%B6%E5%9C%A8-500-%E4%B8%87%E4%BB%A5%E5%86%85">#</a>尽量控制单表数据量的大小，建议控制在 500 万以内</h3><p>500 万并不是 MySQL 数据库的限制，过大会造成修改表结构，备份，恢复都会有很大的问题。</p>
<p>可以用历史数据归档（应用于日志数据），分库分表（应用于业务数据）等手段来控制数据量大小</p>
<h3 id="谨慎使用-MySQL-分区表"><a href="#谨慎使用-MySQL-分区表" class="headerlink" title="#谨慎使用 MySQL 分区表"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E8%B0%A8%E6%85%8E%E4%BD%BF%E7%94%A8-mysql-%E5%88%86%E5%8C%BA%E8%A1%A8">#</a>谨慎使用 MySQL 分区表</h3><p>分区表在物理上表现为多个文件，在逻辑上表现为一个表；</p>
<p>谨慎选择分区键，跨分区查询效率可能更低；</p>
<p>建议采用物理分表的方式管理大数据。</p>
<h3 id="经常一起使用的列放到一个表中"><a href="#经常一起使用的列放到一个表中" class="headerlink" title="#经常一起使用的列放到一个表中"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E7%BB%8F%E5%B8%B8%E4%B8%80%E8%B5%B7%E4%BD%BF%E7%94%A8%E7%9A%84%E5%88%97%E6%94%BE%E5%88%B0%E4%B8%80%E4%B8%AA%E8%A1%A8%E4%B8%AD">#</a>经常一起使用的列放到一个表中</h3><p>避免更多的关联操作。</p>
<h3 id="禁止在表中建立预留字段"><a href="#禁止在表中建立预留字段" class="headerlink" title="#禁止在表中建立预留字段"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E7%A6%81%E6%AD%A2%E5%9C%A8%E8%A1%A8%E4%B8%AD%E5%BB%BA%E7%AB%8B%E9%A2%84%E7%95%99%E5%AD%97%E6%AE%B5">#</a>禁止在表中建立预留字段</h3><ul>
<li>预留字段的命名很难做到见名识义。</li>
<li>预留字段无法确认存储的数据类型，所以无法选择合适的类型。</li>
<li>对预留字段类型的修改，会对表进行锁定。</li>
</ul>
<h3 id="禁止在数据库中存储文件（比如图片）这类大的二进制数据"><a href="#禁止在数据库中存储文件（比如图片）这类大的二进制数据" class="headerlink" title="#禁止在数据库中存储文件（比如图片）这类大的二进制数据"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E7%A6%81%E6%AD%A2%E5%9C%A8%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%AD%E5%AD%98%E5%82%A8%E6%96%87%E4%BB%B6-%E6%AF%94%E5%A6%82%E5%9B%BE%E7%89%87-%E8%BF%99%E7%B1%BB%E5%A4%A7%E7%9A%84%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%95%B0%E6%8D%AE">#</a>禁止在数据库中存储文件（比如图片）这类大的二进制数据</h3><p>在数据库中存储文件会严重影响数据库性能，消耗过多存储空间。</p>
<p>文件（比如图片）这类大的二进制数据通常存储于文件服务器，数据库只存储文件地址信息。</p>
<h3 id="不要被数据库范式所束缚"><a href="#不要被数据库范式所束缚" class="headerlink" title="#不要被数据库范式所束缚"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E4%B8%8D%E8%A6%81%E8%A2%AB%E6%95%B0%E6%8D%AE%E5%BA%93%E8%8C%83%E5%BC%8F%E6%89%80%E6%9D%9F%E7%BC%9A">#</a>不要被数据库范式所束缚</h3><p>一般来说，设计关系数据库时需要满足第三范式，但为了满足第三范式，我们可能会拆分出多张表。而在进行查询时需要对多张表进行关联查询，有时为了提高查询效率，会降低范式的要求，在表中保存一定的冗余信息，也叫做反范式。但要注意反范式一定要适度。</p>
<h3 id="禁止在线上做数据库压力测试"><a href="#禁止在线上做数据库压力测试" class="headerlink" title="#禁止在线上做数据库压力测试"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E7%A6%81%E6%AD%A2%E5%9C%A8%E7%BA%BF%E4%B8%8A%E5%81%9A%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8E%8B%E5%8A%9B%E6%B5%8B%E8%AF%95">#</a>禁止在线上做数据库压力测试</h3><h3 id="禁止从开发环境-测试环境直接连接生产环境数据库"><a href="#禁止从开发环境-测试环境直接连接生产环境数据库" class="headerlink" title="#禁止从开发环境,测试环境直接连接生产环境数据库"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E7%A6%81%E6%AD%A2%E4%BB%8E%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83-%E6%B5%8B%E8%AF%95%E7%8E%AF%E5%A2%83%E7%9B%B4%E6%8E%A5%E8%BF%9E%E6%8E%A5%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E6%95%B0%E6%8D%AE%E5%BA%93">#</a>禁止从开发环境,测试环境直接连接生产环境数据库</h3><p>安全隐患极大，要对生产环境抱有敬畏之心！</p>
<h2 id="数据库字段设计规范"><a href="#数据库字段设计规范" class="headerlink" title="#数据库字段设计规范"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AD%97%E6%AE%B5%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83">#</a>数据库字段设计规范</h2><h3 id="优先选择符合存储需要的最小的数据类型"><a href="#优先选择符合存储需要的最小的数据类型" class="headerlink" title="#优先选择符合存储需要的最小的数据类型"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E4%BC%98%E5%85%88%E9%80%89%E6%8B%A9%E7%AC%A6%E5%90%88%E5%AD%98%E5%82%A8%E9%9C%80%E8%A6%81%E7%9A%84%E6%9C%80%E5%B0%8F%E7%9A%84%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B">#</a>优先选择符合存储需要的最小的数据类型</h3><p>存储字节越小，占用也就空间越小，性能也越好。</p>
<p><strong>a.某些字符串可以转换成数字类型存储比如可以将 IP 地址转换成整型数据。</strong></p>
<p>数字是连续的，性能更好，占用空间也更小。</p>
<p>MySQL 提供了两个方法来处理 ip 地址</p>
<ul>
<li><code>INET_ATON()</code>：把 ip 转为无符号整型 (4-8 位)</li>
<li><code>INET_NTOA()</code> :把整型的 ip 转为地址</li>
</ul>
<p>插入数据前，先用 <code>INET_ATON()</code> 把 ip 地址转为整型，显示数据时，使用 <code>INET_NTOA()</code> 把整型的 ip 地址转为地址显示即可。</p>
<p><strong>b.对于非负型的数据 (如自增 ID,整型 IP，年龄) 来说,要优先使用无符号整型来存储。</strong></p>
<p>无符号相对于有符号可以多出一倍的存储空间</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql">SIGNED <span class="hljs-type">INT</span> <span class="hljs-number">-2147483648</span><span class="hljs-operator">~</span><span class="hljs-number">2147483647</span><br>UNSIGNED <span class="hljs-type">INT</span> <span class="hljs-number">0</span><span class="hljs-operator">~</span><span class="hljs-number">4294967295</span><br></code></pre></td></tr></table></figure>

<p><strong>c.小数值类型（比如年龄、状态表示如 0&#x2F;1）优先使用 TINYINT 类型。</strong></p>
<h3 id="避免使用-TEXT-BLOB-数据类型，最常见的-TEXT-类型可以存储-64k-的数据"><a href="#避免使用-TEXT-BLOB-数据类型，最常见的-TEXT-类型可以存储-64k-的数据" class="headerlink" title="#避免使用 TEXT,BLOB 数据类型，最常见的 TEXT 类型可以存储 64k 的数据"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E9%81%BF%E5%85%8D%E4%BD%BF%E7%94%A8-text-blob-%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B-%E6%9C%80%E5%B8%B8%E8%A7%81%E7%9A%84-text-%E7%B1%BB%E5%9E%8B%E5%8F%AF%E4%BB%A5%E5%AD%98%E5%82%A8-64k-%E7%9A%84%E6%95%B0%E6%8D%AE">#</a>避免使用 TEXT,BLOB 数据类型，最常见的 TEXT 类型可以存储 64k 的数据</h3><p><strong>a. 建议把 BLOB 或是 TEXT 列分离到单独的扩展表中。</strong></p>
<p>MySQL 内存临时表不支持 TEXT、BLOB 这样的大数据类型，如果查询中包含这样的数据，在排序等操作时，就不能使用内存临时表，必须使用磁盘临时表进行。而且对于这种数据，MySQL 还是要进行二次查询，会使 sql 性能变得很差，但是不是说一定不能使用这样的数据类型。</p>
<p>如果一定要使用，建议把 BLOB 或是 TEXT 列分离到单独的扩展表中，查询时一定不要使用 <code>select *</code>而只需要取出必要的列，不需要 TEXT 列的数据时不要对该列进行查询。</p>
<p><strong>2、TEXT 或 BLOB 类型只能使用前缀索引</strong></p>
<p>因为 MySQL 对索引字段长度是有限制的，所以 TEXT 类型只能使用前缀索引，并且 TEXT 列上是不能有默认值的</p>
<h3 id="避免使用-ENUM-类型"><a href="#避免使用-ENUM-类型" class="headerlink" title="#避免使用 ENUM 类型"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E9%81%BF%E5%85%8D%E4%BD%BF%E7%94%A8-enum-%E7%B1%BB%E5%9E%8B">#</a>避免使用 ENUM 类型</h3><ul>
<li>修改 ENUM 值需要使用 ALTER 语句；</li>
<li>ENUM 类型的 ORDER BY 操作效率低，需要额外操作；</li>
<li>ENUM 数据类型存在一些限制比如建议不要使用数值作为 ENUM 的枚举值。</li>
</ul>
<p>相关阅读：<a target="_blank" rel="noopener" href="https://www.zhihu.com/question/404422255/answer/1661698499">是否推荐使用 MySQL 的 enum 类型？ - 架构文摘 - 知乎open in new window</a> 。</p>
<h3 id="尽可能把所有列定义为-NOT-NULL"><a href="#尽可能把所有列定义为-NOT-NULL" class="headerlink" title="#尽可能把所有列定义为 NOT NULL"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E5%B0%BD%E5%8F%AF%E8%83%BD%E6%8A%8A%E6%89%80%E6%9C%89%E5%88%97%E5%AE%9A%E4%B9%89%E4%B8%BA-not-null">#</a>尽可能把所有列定义为 NOT NULL</h3><p>除非有特别的原因使用 NULL 值，应该总是让字段保持 NOT NULL。</p>
<ul>
<li>索引 NULL 列需要额外的空间来保存，所以要占用更多的空间；</li>
<li>进行比较和计算时要对 NULL 值做特别的处理。</li>
</ul>
<p>相关阅读：<a target="_blank" rel="noopener" href="https://opensource.actionsky.com/20190710-mysql/">技术分享 | MySQL 默认值选型（是空，还是 NULL）open in new window</a> 。</p>
<h3 id="一定不要用字符串存储日期"><a href="#一定不要用字符串存储日期" class="headerlink" title="#一定不要用字符串存储日期"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E4%B8%80%E5%AE%9A%E4%B8%8D%E8%A6%81%E7%94%A8%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%AD%98%E5%82%A8%E6%97%A5%E6%9C%9F">#</a>一定不要用字符串存储日期</h3><p>对于日期类型来说， 一定不要用字符串存储日期。可以考虑 DATETIME、TIMESTAMP 和 数值型时间戳。</p>
<p>这三种种方式都有各自的优势，根据实际场景选择最合适的才是王道。下面再对这三种方式做一个简单的对比，以供大家实际开发中选择正确的存放时间的数据类型：</p>
<table>
<thead>
<tr>
<th>类型</th>
<th>存储空间</th>
<th>日期格式</th>
<th>日期范围</th>
<th>是否带时区信息</th>
</tr>
</thead>
<tbody><tr>
<td>DATETIME</td>
<td>5~8 字节</td>
<td>YYYY-MM-DD hh:mm:ss[.fraction]</td>
<td>1000-01-01 00:00:00[.000000] ～ 9999-12-31 23:59:59[.999999]</td>
<td>否</td>
</tr>
<tr>
<td>TIMESTAMP</td>
<td>4~7 字节</td>
<td>YYYY-MM-DD hh:mm:ss[.fraction]</td>
<td>1970-01-01 00:00:01[.000000] ～ 2038-01-19 03:14:07[.999999]</td>
<td>是</td>
</tr>
<tr>
<td>数值型时间戳</td>
<td>4 字节</td>
<td>全数字如 1578707612</td>
<td>1970-01-01 00:00:01 之后的时间</td>
<td>否</td>
</tr>
</tbody></table>
<p>MySQL 时间类型选择的详细介绍请看这篇：<a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/some-thoughts-on-database-storage-time.html">MySQL 时间类型数据存储建议open in new window</a>。</p>
<h3 id="同财务相关的金额类数据必须使用-decimal-类型"><a href="#同财务相关的金额类数据必须使用-decimal-类型" class="headerlink" title="#同财务相关的金额类数据必须使用 decimal 类型"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E5%90%8C%E8%B4%A2%E5%8A%A1%E7%9B%B8%E5%85%B3%E7%9A%84%E9%87%91%E9%A2%9D%E7%B1%BB%E6%95%B0%E6%8D%AE%E5%BF%85%E9%A1%BB%E4%BD%BF%E7%94%A8-decimal-%E7%B1%BB%E5%9E%8B">#</a>同财务相关的金额类数据必须使用 decimal 类型</h3><ul>
<li><strong>非精准浮点</strong>：float,double</li>
<li><strong>精准浮点</strong>：decimal</li>
</ul>
<p>decimal 类型为精准浮点数，在计算时不会丢失精度。占用空间由定义的宽度决定，每 4 个字节可以存储 9 位数字，并且小数点要占用一个字节。并且，decimal 可用于存储比 bigint 更大的整型数据</p>
<p>不过， 由于 decimal 需要额外的空间和计算开销，应该尽量只在需要对数据进行精确计算时才使用 decimal 。</p>
<h3 id="单表不要包含过多字段"><a href="#单表不要包含过多字段" class="headerlink" title="#单表不要包含过多字段"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E5%8D%95%E8%A1%A8%E4%B8%8D%E8%A6%81%E5%8C%85%E5%90%AB%E8%BF%87%E5%A4%9A%E5%AD%97%E6%AE%B5">#</a>单表不要包含过多字段</h3><p>如果一个表包含过多字段的话，可以考虑将其分解成多个表，必要时增加中间表进行关联。</p>
<h2 id="索引设计规范"><a href="#索引设计规范" class="headerlink" title="#索引设计规范"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E7%B4%A2%E5%BC%95%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83">#</a>索引设计规范</h2><h3 id="限制每张表上的索引数量-建议单张表索引不超过-5-个"><a href="#限制每张表上的索引数量-建议单张表索引不超过-5-个" class="headerlink" title="#限制每张表上的索引数量,建议单张表索引不超过 5 个"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E9%99%90%E5%88%B6%E6%AF%8F%E5%BC%A0%E8%A1%A8%E4%B8%8A%E7%9A%84%E7%B4%A2%E5%BC%95%E6%95%B0%E9%87%8F-%E5%BB%BA%E8%AE%AE%E5%8D%95%E5%BC%A0%E8%A1%A8%E7%B4%A2%E5%BC%95%E4%B8%8D%E8%B6%85%E8%BF%87-5-%E4%B8%AA">#</a>限制每张表上的索引数量,建议单张表索引不超过 5 个</h3><p>索引并不是越多越好！索引可以提高效率同样可以降低效率。</p>
<p>索引可以增加查询效率，但同样也会降低插入和更新的效率，甚至有些情况下会降低查询效率。</p>
<p>因为 MySQL 优化器在选择如何优化查询时，会根据统一信息，对每一个可以用到的索引来进行评估，以生成出一个最好的执行计划，如果同时有很多个索引都可以用于查询，就会增加 MySQL 优化器生成执行计划的时间，同样会降低查询性能。</p>
<h3 id="禁止使用全文索引"><a href="#禁止使用全文索引" class="headerlink" title="#禁止使用全文索引"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E7%A6%81%E6%AD%A2%E4%BD%BF%E7%94%A8%E5%85%A8%E6%96%87%E7%B4%A2%E5%BC%95">#</a>禁止使用全文索引</h3><p>全文索引不适用于 OLTP 场景。</p>
<h3 id="禁止给表中的每一列都建立单独的索引"><a href="#禁止给表中的每一列都建立单独的索引" class="headerlink" title="#禁止给表中的每一列都建立单独的索引"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E7%A6%81%E6%AD%A2%E7%BB%99%E8%A1%A8%E4%B8%AD%E7%9A%84%E6%AF%8F%E4%B8%80%E5%88%97%E9%83%BD%E5%BB%BA%E7%AB%8B%E5%8D%95%E7%8B%AC%E7%9A%84%E7%B4%A2%E5%BC%95">#</a>禁止给表中的每一列都建立单独的索引</h3><p>5.6 版本之前，一个 sql 只能使用到一个表中的一个索引，5.6 以后，虽然有了合并索引的优化方式，但是还是远远没有使用一个联合索引的查询方式好。</p>
<h3 id="每个-InnoDB-表必须有个主键"><a href="#每个-InnoDB-表必须有个主键" class="headerlink" title="#每个 InnoDB 表必须有个主键"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E6%AF%8F%E4%B8%AA-innodb-%E8%A1%A8%E5%BF%85%E9%A1%BB%E6%9C%89%E4%B8%AA%E4%B8%BB%E9%94%AE">#</a>每个 InnoDB 表必须有个主键</h3><p>InnoDB 是一种索引组织表：数据的存储的逻辑顺序和索引的顺序是相同的。每个表都可以有多个索引，但是表的存储顺序只能有一种。</p>
<p>InnoDB 是按照主键索引的顺序来组织表的</p>
<ul>
<li>不要使用更新频繁的列作为主键，不使用多列主键（相当于联合索引）</li>
<li>不要使用 UUID,MD5,HASH,字符串列作为主键（无法保证数据的顺序增长）</li>
<li>主键建议使用自增 ID 值</li>
</ul>
<h3 id="常见索引列建议"><a href="#常见索引列建议" class="headerlink" title="#常见索引列建议"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E5%B8%B8%E8%A7%81%E7%B4%A2%E5%BC%95%E5%88%97%E5%BB%BA%E8%AE%AE">#</a>常见索引列建议</h3><ul>
<li>出现在 SELECT、UPDATE、DELETE 语句的 WHERE 从句中的列</li>
<li>包含在 ORDER BY、GROUP BY、DISTINCT 中的字段</li>
<li>并不要将符合 1 和 2 中的字段的列都建立一个索引， 通常将 1、2 中的字段建立联合索引效果更好</li>
<li>多表 join 的关联列</li>
</ul>
<h3 id="如何选择索引列的顺序"><a href="#如何选择索引列的顺序" class="headerlink" title="#如何选择索引列的顺序"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E5%A6%82%E4%BD%95%E9%80%89%E6%8B%A9%E7%B4%A2%E5%BC%95%E5%88%97%E7%9A%84%E9%A1%BA%E5%BA%8F">#</a>如何选择索引列的顺序</h3><p>建立索引的目的是：希望通过索引进行数据查找，减少随机 IO，增加查询性能 ，索引能过滤出越少的数据，则从磁盘中读入的数据也就越少。</p>
<ul>
<li>区分度最高的放在联合索引的最左侧（区分度&#x3D;列中不同值的数量&#x2F;列的总行数）</li>
<li>尽量把字段长度小的列放在联合索引的最左侧（因为字段长度越小，一页能存储的数据量越大，IO 性能也就越好）</li>
<li>使用最频繁的列放到联合索引的左侧（这样可以比较少的建立一些索引）</li>
</ul>
<h3 id="避免建立冗余索引和重复索引（增加了查询优化器生成执行计划的时间）"><a href="#避免建立冗余索引和重复索引（增加了查询优化器生成执行计划的时间）" class="headerlink" title="#避免建立冗余索引和重复索引（增加了查询优化器生成执行计划的时间）"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E9%81%BF%E5%85%8D%E5%BB%BA%E7%AB%8B%E5%86%97%E4%BD%99%E7%B4%A2%E5%BC%95%E5%92%8C%E9%87%8D%E5%A4%8D%E7%B4%A2%E5%BC%95-%E5%A2%9E%E5%8A%A0%E4%BA%86%E6%9F%A5%E8%AF%A2%E4%BC%98%E5%8C%96%E5%99%A8%E7%94%9F%E6%88%90%E6%89%A7%E8%A1%8C%E8%AE%A1%E5%88%92%E7%9A%84%E6%97%B6%E9%97%B4">#</a>避免建立冗余索引和重复索引（增加了查询优化器生成执行计划的时间）</h3><ul>
<li>重复索引示例：primary key(id)、index(id)、unique index(id)</li>
<li>冗余索引示例：index(a,b,c)、index(a,b)、index(a)</li>
</ul>
<h3 id="对于频繁的查询优先考虑使用覆盖索引"><a href="#对于频繁的查询优先考虑使用覆盖索引" class="headerlink" title="#对于频繁的查询优先考虑使用覆盖索引"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E5%AF%B9%E4%BA%8E%E9%A2%91%E7%B9%81%E7%9A%84%E6%9F%A5%E8%AF%A2%E4%BC%98%E5%85%88%E8%80%83%E8%99%91%E4%BD%BF%E7%94%A8%E8%A6%86%E7%9B%96%E7%B4%A2%E5%BC%95">#</a>对于频繁的查询优先考虑使用覆盖索引</h3><blockquote>
<p>  覆盖索引：就是包含了所有查询字段 (where,select,order by,group by 包含的字段) 的索引</p>
</blockquote>
<p><strong>覆盖索引的好处：</strong></p>
<ul>
<li><strong>避免 InnoDB 表进行索引的二次查询:</strong> InnoDB 是以聚集索引的顺序来存储的，对于 InnoDB 来说，二级索引在叶子节点中所保存的是行的主键信息，如果是用二级索引查询数据的话，在查找到相应的键值后，还要通过主键进行二次查询才能获取我们真实所需要的数据。而在覆盖索引中，二级索引的键值中可以获取所有的数据，避免了对主键的二次查询 ，减少了 IO 操作，提升了查询效率。</li>
<li><strong>可以把随机 IO 变成顺序 IO 加快查询效率:</strong> 由于覆盖索引是按键值的顺序存储的，对于 IO 密集型的范围查找来说，对比随机从磁盘读取每一行的数据 IO 要少的多，因此利用覆盖索引在访问时也可以把磁盘的随机读取的 IO 转变成索引查找的顺序 IO。</li>
</ul>
<hr>
<h3 id="索引-SET-规范"><a href="#索引-SET-规范" class="headerlink" title="#索引 SET 规范"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E7%B4%A2%E5%BC%95-set-%E8%A7%84%E8%8C%83">#</a>索引 SET 规范</h3><p><strong>尽量避免使用外键约束</strong></p>
<ul>
<li>不建议使用外键约束（foreign key），但一定要在表与表之间的关联键上建立索引</li>
<li>外键可用于保证数据的参照完整性，但建议在业务端实现</li>
<li>外键会影响父表和子表的写操作从而降低性能</li>
</ul>
<h2 id="数据库-SQL-开发规范"><a href="#数据库-SQL-开发规范" class="headerlink" title="#数据库 SQL 开发规范"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E6%95%B0%E6%8D%AE%E5%BA%93-sql-%E5%BC%80%E5%8F%91%E8%A7%84%E8%8C%83">#</a>数据库 SQL 开发规范</h2><h3 id="尽量不在数据库做运算，复杂运算需移到业务应用里完成"><a href="#尽量不在数据库做运算，复杂运算需移到业务应用里完成" class="headerlink" title="#尽量不在数据库做运算，复杂运算需移到业务应用里完成"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E5%B0%BD%E9%87%8F%E4%B8%8D%E5%9C%A8%E6%95%B0%E6%8D%AE%E5%BA%93%E5%81%9A%E8%BF%90%E7%AE%97-%E5%A4%8D%E6%9D%82%E8%BF%90%E7%AE%97%E9%9C%80%E7%A7%BB%E5%88%B0%E4%B8%9A%E5%8A%A1%E5%BA%94%E7%94%A8%E9%87%8C%E5%AE%8C%E6%88%90">#</a>尽量不在数据库做运算，复杂运算需移到业务应用里完成</h3><p>尽量不在数据库做运算，复杂运算需移到业务应用里完成。这样可以避免数据库的负担过重，影响数据库的性能和稳定性。数据库的主要作用是存储和管理数据，而不是处理数据。</p>
<h3 id="优化对性能影响较大的-SQL-语句"><a href="#优化对性能影响较大的-SQL-语句" class="headerlink" title="#优化对性能影响较大的 SQL 语句"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E4%BC%98%E5%8C%96%E5%AF%B9%E6%80%A7%E8%83%BD%E5%BD%B1%E5%93%8D%E8%BE%83%E5%A4%A7%E7%9A%84-sql-%E8%AF%AD%E5%8F%A5">#</a>优化对性能影响较大的 SQL 语句</h3><p>要找到最需要优化的 SQL 语句。要么是使用最频繁的语句，要么是优化后提高最明显的语句，可以通过查询 MySQL 的慢查询日志来发现需要进行优化的 SQL 语句。</p>
<h3 id="充分利用表上已经存在的索引"><a href="#充分利用表上已经存在的索引" class="headerlink" title="#充分利用表上已经存在的索引"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E5%85%85%E5%88%86%E5%88%A9%E7%94%A8%E8%A1%A8%E4%B8%8A%E5%B7%B2%E7%BB%8F%E5%AD%98%E5%9C%A8%E7%9A%84%E7%B4%A2%E5%BC%95">#</a>充分利用表上已经存在的索引</h3><p>避免使用双%号的查询条件。如：<code>a like &#39;%123%&#39;</code>，（如果无前置%,只有后置%，是可以用到列上的索引的）</p>
<p>一个 SQL 只能利用到复合索引中的一列进行范围查询。如：有 a,b,c 列的联合索引，在查询条件中有 a 列的范围查询，则在 b,c 列上的索引将不会被用到。</p>
<p>在定义联合索引时，如果 a 列要用到范围查找的话，就要把 a 列放到联合索引的右侧，使用 left join 或 not exists 来优化 not in 操作，因为 not in 也通常会使用索引失效。</p>
<h3 id="禁止使用-SELECT-必须使用-SELECT-查询"><a href="#禁止使用-SELECT-必须使用-SELECT-查询" class="headerlink" title="#禁止使用 SELECT * 必须使用 SELECT &lt;字段列表&gt; 查询"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E7%A6%81%E6%AD%A2%E4%BD%BF%E7%94%A8-select-%E5%BF%85%E9%A1%BB%E4%BD%BF%E7%94%A8-select-%E5%AD%97%E6%AE%B5%E5%88%97%E8%A1%A8-%E6%9F%A5%E8%AF%A2">#</a>禁止使用 SELECT * 必须使用 SELECT &lt;字段列表&gt; 查询</h3><ul>
<li><code>SELECT *</code> 会消耗更多的 CPU。</li>
<li><code>SELECT *</code> 无用字段增加网络带宽资源消耗，增加数据传输时间，尤其是大字段（如 varchar、blob、text）。</li>
<li><code>SELECT *</code> 无法使用 MySQL 优化器覆盖索引的优化（基于 MySQL 优化器的“覆盖索引”策略又是速度极快，效率极高，业界极为推荐的查询优化方式）</li>
<li><code>SELECT &lt;字段列表&gt;</code> 可减少表结构变更带来的影响、</li>
</ul>
<h3 id="禁止使用不含字段列表的-INSERT-语句"><a href="#禁止使用不含字段列表的-INSERT-语句" class="headerlink" title="#禁止使用不含字段列表的 INSERT 语句"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E7%A6%81%E6%AD%A2%E4%BD%BF%E7%94%A8%E4%B8%8D%E5%90%AB%E5%AD%97%E6%AE%B5%E5%88%97%E8%A1%A8%E7%9A%84-insert-%E8%AF%AD%E5%8F%A5">#</a>禁止使用不含字段列表的 INSERT 语句</h3><p>如：</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> t <span class="hljs-keyword">values</span> (<span class="hljs-string">&#x27;a&#x27;</span>,<span class="hljs-string">&#x27;b&#x27;</span>,<span class="hljs-string">&#x27;c&#x27;</span>);<br></code></pre></td></tr></table></figure>

<p>应使用：</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> t(c1,c2,c3) <span class="hljs-keyword">values</span> (<span class="hljs-string">&#x27;a&#x27;</span>,<span class="hljs-string">&#x27;b&#x27;</span>,<span class="hljs-string">&#x27;c&#x27;</span>);<br></code></pre></td></tr></table></figure>

<h3 id="建议使用预编译语句进行数据库操作"><a href="#建议使用预编译语句进行数据库操作" class="headerlink" title="#建议使用预编译语句进行数据库操作"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E5%BB%BA%E8%AE%AE%E4%BD%BF%E7%94%A8%E9%A2%84%E7%BC%96%E8%AF%91%E8%AF%AD%E5%8F%A5%E8%BF%9B%E8%A1%8C%E6%95%B0%E6%8D%AE%E5%BA%93%E6%93%8D%E4%BD%9C">#</a>建议使用预编译语句进行数据库操作</h3><ul>
<li>预编译语句可以重复使用这些计划，减少 SQL 编译所需要的时间，还可以解决动态 SQL 所带来的 SQL 注入的问题。</li>
<li>只传参数，比传递 SQL 语句更高效。</li>
<li>相同语句可以一次解析，多次使用，提高处理效率。</li>
</ul>
<h3 id="避免数据类型的隐式转换"><a href="#避免数据类型的隐式转换" class="headerlink" title="#避免数据类型的隐式转换"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E9%81%BF%E5%85%8D%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E7%9A%84%E9%9A%90%E5%BC%8F%E8%BD%AC%E6%8D%A2">#</a>避免数据类型的隐式转换</h3><p>隐式转换会导致索引失效如:</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">select</span> name,phone <span class="hljs-keyword">from</span> customer <span class="hljs-keyword">where</span> id <span class="hljs-operator">=</span> <span class="hljs-string">&#x27;111&#x27;</span>;<br></code></pre></td></tr></table></figure>

<p>详细解读可以看：<a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/index-invalidation-caused-by-implicit-conversion.html">MySQL 中的隐式转换造成的索引失效</a> 这篇文章。</p>
<h3 id="避免使用子查询，可以把子查询优化为-join-操作"><a href="#避免使用子查询，可以把子查询优化为-join-操作" class="headerlink" title="#避免使用子查询，可以把子查询优化为 join 操作"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E9%81%BF%E5%85%8D%E4%BD%BF%E7%94%A8%E5%AD%90%E6%9F%A5%E8%AF%A2-%E5%8F%AF%E4%BB%A5%E6%8A%8A%E5%AD%90%E6%9F%A5%E8%AF%A2%E4%BC%98%E5%8C%96%E4%B8%BA-join-%E6%93%8D%E4%BD%9C">#</a>避免使用子查询，可以把子查询优化为 join 操作</h3><p>通常子查询在 in 子句中，且子查询中为简单 SQL(不包含 union、group by、order by、limit 从句) 时,才可以把子查询转化为关联查询进行优化。</p>
<p><strong>子查询性能差的原因：</strong> 子查询的结果集无法使用索引，通常子查询的结果集会被存储到临时表中，不论是内存临时表还是磁盘临时表都不会存在索引，所以查询性能会受到一定的影响。特别是对于返回结果集比较大的子查询，其对查询性能的影响也就越大。由于子查询会产生大量的临时表也没有索引，所以会消耗过多的 CPU 和 IO 资源，产生大量的慢查询。</p>
<h3 id="避免使用-JOIN-关联太多的表"><a href="#避免使用-JOIN-关联太多的表" class="headerlink" title="#避免使用 JOIN 关联太多的表"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E9%81%BF%E5%85%8D%E4%BD%BF%E7%94%A8-join-%E5%85%B3%E8%81%94%E5%A4%AA%E5%A4%9A%E7%9A%84%E8%A1%A8">#</a>避免使用 JOIN 关联太多的表</h3><p>对于 MySQL 来说，是存在关联缓存的，缓存的大小可以由 join_buffer_size 参数进行设置。</p>
<p>在 MySQL 中，对于同一个 SQL 多关联（join）一个表，就会多分配一个关联缓存，如果在一个 SQL 中关联的表越多，所占用的内存也就越大。</p>
<p>如果程序中大量的使用了多表关联的操作，同时 join_buffer_size 设置的也不合理的情况下，就容易造成服务器内存溢出的情况，就会影响到服务器数据库性能的稳定性。</p>
<p>同时对于关联操作来说，会产生临时表操作，影响查询效率，MySQL 最多允许关联 61 个表，建议不超过 5 个。</p>
<h3 id="减少同数据库的交互次数"><a href="#减少同数据库的交互次数" class="headerlink" title="#减少同数据库的交互次数"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E5%87%8F%E5%B0%91%E5%90%8C%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9A%84%E4%BA%A4%E4%BA%92%E6%AC%A1%E6%95%B0">#</a>减少同数据库的交互次数</h3><p>数据库更适合处理批量操作，合并多个相同的操作到一起，可以提高处理效率。</p>
<h3 id="对应同一列进行-or-判断时，使用-in-代替-or"><a href="#对应同一列进行-or-判断时，使用-in-代替-or" class="headerlink" title="#对应同一列进行 or 判断时，使用 in 代替 or"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E5%AF%B9%E5%BA%94%E5%90%8C%E4%B8%80%E5%88%97%E8%BF%9B%E8%A1%8C-or-%E5%88%A4%E6%96%AD%E6%97%B6-%E4%BD%BF%E7%94%A8-in-%E4%BB%A3%E6%9B%BF-or">#</a>对应同一列进行 or 判断时，使用 in 代替 or</h3><p>in 的值不要超过 500 个，in 操作可以更有效的利用索引，or 大多数情况下很少能利用到索引。</p>
<h3 id="禁止使用-order-by-rand-进行随机排序"><a href="#禁止使用-order-by-rand-进行随机排序" class="headerlink" title="#禁止使用 order by rand() 进行随机排序"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E7%A6%81%E6%AD%A2%E4%BD%BF%E7%94%A8-order-by-rand-%E8%BF%9B%E8%A1%8C%E9%9A%8F%E6%9C%BA%E6%8E%92%E5%BA%8F">#</a>禁止使用 order by rand() 进行随机排序</h3><p>order by rand() 会把表中所有符合条件的数据装载到内存中，然后在内存中对所有数据根据随机生成的值进行排序，并且可能会对每一行都生成一个随机值，如果满足条件的数据集非常大，就会消耗大量的 CPU 和 IO 及内存资源。</p>
<p>推荐在程序中获取一个随机值，然后从数据库中获取数据的方式。</p>
<h3 id="WHERE-从句中禁止对列进行函数转换和计算"><a href="#WHERE-从句中禁止对列进行函数转换和计算" class="headerlink" title="#WHERE 从句中禁止对列进行函数转换和计算"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#where-%E4%BB%8E%E5%8F%A5%E4%B8%AD%E7%A6%81%E6%AD%A2%E5%AF%B9%E5%88%97%E8%BF%9B%E8%A1%8C%E5%87%BD%E6%95%B0%E8%BD%AC%E6%8D%A2%E5%92%8C%E8%AE%A1%E7%AE%97">#</a>WHERE 从句中禁止对列进行函数转换和计算</h3><p>对列进行函数转换或计算时会导致无法使用索引</p>
<p><strong>不推荐：</strong></p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">where</span> <span class="hljs-type">date</span>(create_time)<span class="hljs-operator">=</span><span class="hljs-string">&#x27;20190101&#x27;</span><br></code></pre></td></tr></table></figure>

<p><strong>推荐：</strong></p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">where</span> create_time <span class="hljs-operator">&gt;=</span> <span class="hljs-string">&#x27;20190101&#x27;</span> <span class="hljs-keyword">and</span> create_time <span class="hljs-operator">&lt;</span> <span class="hljs-string">&#x27;20190102&#x27;</span><br></code></pre></td></tr></table></figure>

<h3 id="在明显不会有重复值时使用-UNION-ALL-而不是-UNION"><a href="#在明显不会有重复值时使用-UNION-ALL-而不是-UNION" class="headerlink" title="#在明显不会有重复值时使用 UNION ALL 而不是 UNION"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E5%9C%A8%E6%98%8E%E6%98%BE%E4%B8%8D%E4%BC%9A%E6%9C%89%E9%87%8D%E5%A4%8D%E5%80%BC%E6%97%B6%E4%BD%BF%E7%94%A8-union-all-%E8%80%8C%E4%B8%8D%E6%98%AF-union">#</a>在明显不会有重复值时使用 UNION ALL 而不是 UNION</h3><ul>
<li>UNION 会把两个结果集的所有数据放到临时表中后再进行去重操作</li>
<li>UNION ALL 不会再对结果集进行去重操作</li>
</ul>
<h3 id="拆分复杂的大-SQL-为多个小-SQL"><a href="#拆分复杂的大-SQL-为多个小-SQL" class="headerlink" title="#拆分复杂的大 SQL 为多个小 SQL"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E6%8B%86%E5%88%86%E5%A4%8D%E6%9D%82%E7%9A%84%E5%A4%A7-sql-%E4%B8%BA%E5%A4%9A%E4%B8%AA%E5%B0%8F-sql">#</a>拆分复杂的大 SQL 为多个小 SQL</h3><ul>
<li>大 SQL 逻辑上比较复杂，需要占用大量 CPU 进行计算的 SQL</li>
<li>MySQL 中，一个 SQL 只能使用一个 CPU 进行计算</li>
<li>SQL 拆分后可以通过并行执行来提高处理效率</li>
</ul>
<h3 id="程序连接不同的数据库使用不同的账号，禁止跨库查询"><a href="#程序连接不同的数据库使用不同的账号，禁止跨库查询" class="headerlink" title="#程序连接不同的数据库使用不同的账号，禁止跨库查询"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E7%A8%8B%E5%BA%8F%E8%BF%9E%E6%8E%A5%E4%B8%8D%E5%90%8C%E7%9A%84%E6%95%B0%E6%8D%AE%E5%BA%93%E4%BD%BF%E7%94%A8%E4%B8%8D%E5%90%8C%E7%9A%84%E8%B4%A6%E5%8F%B7-%E7%A6%81%E6%AD%A2%E8%B7%A8%E5%BA%93%E6%9F%A5%E8%AF%A2">#</a>程序连接不同的数据库使用不同的账号，禁止跨库查询</h3><ul>
<li>为数据库迁移和分库分表留出余地</li>
<li>降低业务耦合度</li>
<li>避免权限过大而产生的安全风险</li>
</ul>
<h2 id="数据库操作行为规范"><a href="#数据库操作行为规范" class="headerlink" title="#数据库操作行为规范"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E6%95%B0%E6%8D%AE%E5%BA%93%E6%93%8D%E4%BD%9C%E8%A1%8C%E4%B8%BA%E8%A7%84%E8%8C%83">#</a>数据库操作行为规范</h2><h3 id="超-100-万行的批量写-UPDATE-DELETE-INSERT-操作-要分批多次进行操作"><a href="#超-100-万行的批量写-UPDATE-DELETE-INSERT-操作-要分批多次进行操作" class="headerlink" title="#超 100 万行的批量写 (UPDATE,DELETE,INSERT) 操作,要分批多次进行操作"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E8%B6%85-100-%E4%B8%87%E8%A1%8C%E7%9A%84%E6%89%B9%E9%87%8F%E5%86%99-update-delete-insert-%E6%93%8D%E4%BD%9C-%E8%A6%81%E5%88%86%E6%89%B9%E5%A4%9A%E6%AC%A1%E8%BF%9B%E8%A1%8C%E6%93%8D%E4%BD%9C">#</a>超 100 万行的批量写 (UPDATE,DELETE,INSERT) 操作,要分批多次进行操作</h3><p><strong>大批量操作可能会造成严重的主从延迟</strong></p>
<p>主从环境中,大批量操作可能会造成严重的主从延迟，大批量的写操作一般都需要执行一定长的时间，而只有当主库上执行完成后，才会在其他从库上执行，所以会造成主库与从库长时间的延迟情况</p>
<p><strong>binlog 日志为 row 格式时会产生大量的日志</strong></p>
<p>大批量写操作会产生大量日志，特别是对于 row 格式二进制数据而言，由于在 row 格式中会记录每一行数据的修改，我们一次修改的数据越多，产生的日志量也就会越多，日志的传输和恢复所需要的时间也就越长，这也是造成主从延迟的一个原因</p>
<p><strong>避免产生大事务操作</strong></p>
<p>大批量修改数据，一定是在一个事务中进行的，这就会造成表中大批量数据进行锁定，从而导致大量的阻塞，阻塞会对 MySQL 的性能产生非常大的影响。</p>
<p>特别是长时间的阻塞会占满所有数据库的可用连接，这会使生产环境中的其他应用无法连接到数据库，因此一定要注意大批量写操作要进行分批</p>
<h3 id="对于大表使用-pt-online-schema-change-修改表结构"><a href="#对于大表使用-pt-online-schema-change-修改表结构" class="headerlink" title="#对于大表使用 pt-online-schema-change 修改表结构"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E5%AF%B9%E4%BA%8E%E5%A4%A7%E8%A1%A8%E4%BD%BF%E7%94%A8-pt-online-schema-change-%E4%BF%AE%E6%94%B9%E8%A1%A8%E7%BB%93%E6%9E%84">#</a>对于大表使用 pt-online-schema-change 修改表结构</h3><ul>
<li>避免大表修改产生的主从延迟</li>
<li>避免在对表字段进行修改时进行锁表</li>
</ul>
<p>对大表数据结构的修改一定要谨慎，会造成严重的锁表操作，尤其是生产环境，是不能容忍的。</p>
<p>pt-online-schema-change 它会首先建立一个与原表结构相同的新表，并且在新表上进行表结构的修改，然后再把原表中的数据复制到新表中，并在原表中增加一些触发器。把原表中新增的数据也复制到新表中，在行所有数据复制完成之后，把新表命名成原表，并把原来的表删除掉。把原来一个 DDL 操作，分解成多个小的批次进行。</p>
<h3 id="禁止为程序使用的账号赋予-super-权限"><a href="#禁止为程序使用的账号赋予-super-权限" class="headerlink" title="#禁止为程序使用的账号赋予 super 权限"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E7%A6%81%E6%AD%A2%E4%B8%BA%E7%A8%8B%E5%BA%8F%E4%BD%BF%E7%94%A8%E7%9A%84%E8%B4%A6%E5%8F%B7%E8%B5%8B%E4%BA%88-super-%E6%9D%83%E9%99%90">#</a>禁止为程序使用的账号赋予 super 权限</h3><ul>
<li>当达到最大连接数限制时，还运行 1 个有 super 权限的用户连接</li>
<li>super 权限只能留给 DBA 处理问题的账号使用</li>
</ul>
<h3 id="对于程序连接数据库账号-遵循权限最小原则"><a href="#对于程序连接数据库账号-遵循权限最小原则" class="headerlink" title="#对于程序连接数据库账号,遵循权限最小原则"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E5%AF%B9%E4%BA%8E%E7%A8%8B%E5%BA%8F%E8%BF%9E%E6%8E%A5%E6%95%B0%E6%8D%AE%E5%BA%93%E8%B4%A6%E5%8F%B7-%E9%81%B5%E5%BE%AA%E6%9D%83%E9%99%90%E6%9C%80%E5%B0%8F%E5%8E%9F%E5%88%99">#</a>对于程序连接数据库账号,遵循权限最小原则</h3><ul>
<li>程序使用数据库账号只能在一个 DB 下使用，不准跨库</li>
<li>程序使用的账号原则上不准有 drop 权限</li>
</ul>
<h2 id="推荐阅读"><a href="#推荐阅读" class="headerlink" title="#推荐阅读"></a><a target="_blank" rel="noopener" href="https://javaguide.cn/database/mysql/mysql-high-performance-optimization-specification-recommendations.html#%E6%8E%A8%E8%8D%90%E9%98%85%E8%AF%BB">#</a>推荐阅读</h2>
                
              </div>
            
            <hr/>
            <div>
              <div class="post-metas my-3">
  
    <div class="post-meta mr-3 d-flex align-items-center">
      <i class="iconfont icon-category"></i>
      

<span class="category-chains">
  
  
    
      <span class="category-chain">
        
  <a href="/categories/MySQL/" class="category-chain-item">MySQL</a>
  
  

      </span>
    
  
</span>

    </div>
  
  
    <div class="post-meta">
      <i class="iconfont icon-tags"></i>
      
        <a href="/tags/MySQL/" class="print-no-link">#MySQL</a>
      
    </div>
  
</div>


              
  

  <div class="license-box my-3">
    <div class="license-title">
      <div>MySQL高性能优化规范建议总结</div>
      <div>http://example.com/2021/04/06/DB/MySQL/11. MySQL高性能优化规范建议总结/</div>
    </div>
    <div class="license-meta">
      
        <div class="license-meta-item">
          <div>作者</div>
          <div>fyy</div>
        </div>
      
      
        <div class="license-meta-item license-meta-date">
          <div>发布于</div>
          <div>2021年4月6日</div>
        </div>
      
      
      
        <div class="license-meta-item">
          <div>许可协议</div>
          <div>
            
              
              
                <a class="print-no-link" target="_blank" href="https://creativecommons.org/licenses/by/4.0/">
                  <span class="hint--top hint--rounded" aria-label="BY - 署名">
                    <i class="iconfont icon-by"></i>
                  </span>
                </a>
              
            
          </div>
        </div>
      
    </div>
    <div class="license-icon iconfont"></div>
  </div>



              
                <div class="post-prevnext my-3">
                  <article class="post-prev col-6">
                    
                    
                      <a href="/2021/04/06/DB/MySQL/10.%20MYSQL%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/" title="MySQL基础">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">MySQL基础</span>
                        <span class="visible-mobile">上一篇</span>
                      </a>
                    
                  </article>
                  <article class="post-next col-6">
                    
                    
                      <a href="/2021/04/06/DB/MySQL/2.%20MySQL%E5%AD%97%E6%AE%B5%E7%B1%BB%E5%9E%8B/" title="MySQL字段类型">
                        <span class="hidden-mobile">MySQL字段类型</span>
                        <span class="visible-mobile">下一篇</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </article>
                </div>
              
            </div>

            
  
  
    <article id="comments" lazyload>
      
  <div id="valine"></div>
  <script type="text/javascript">
    Fluid.utils.loadComments('#valine', function() {
      Fluid.utils.createScript('https://lib.baomitu.com/valine/1.5.1/Valine.min.js', function() {
        var options = Object.assign(
          {"appId":"ufph8TbmK43d1JfQxLvss4KY-MdYXbMMI","appKey":"96APiPOtZEiCQlv5h3jjZfbC","path":"window.location.pathname","placeholder":null,"avatar":"retro","meta":["nick","mail","link"],"requiredFields":[],"pageSize":10,"lang":"zh-CN","highlight":false,"recordIP":false,"serverURLs":"","emojiCDN":null,"emojiMaps":null,"enableQQ":false},
          {
            el: "#valine",
            path: window.location.pathname
          }
        )
        new Valine(options);
        Fluid.utils.waitElementVisible('#valine .vcontent', () => {
          var imgSelector = '#valine .vcontent img:not(.vemoji)';
          Fluid.plugins.imageCaption(imgSelector);
          Fluid.plugins.fancyBox(imgSelector);
        })
      });
    });
  </script>
  <noscript>Please enable JavaScript to view the comments</noscript>


    </article>
  


          </article>
        </div>
      </div>
    </div>

    <div class="side-col d-none d-lg-block col-lg-2">
      
  <aside class="sidebar" style="margin-left: -1rem">
    <div id="toc">
  <p class="toc-header">
    <i class="iconfont icon-list"></i>
    <span>目录</span>
  </p>
  <div class="toc-body" id="toc-body"></div>
</div>



  </aside>


    </div>
  </div>
</div>





  



  



  



  



  







    

    
      <a id="scroll-top-button" aria-label="TOP" href="#" role="button">
        <i class="iconfont icon-arrowup" aria-hidden="true"></i>
      </a>
    

    
      <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">搜索</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v" for="local-search-input">关键词</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>

    

    
  </main>

  <footer>
    <div class="footer-inner">
  
    <div class="footer-content">
       <a href="https://hexo.io" target="_blank" rel="nofollow noopener"><span>Hexo</span></a> <i class="iconfont icon-love"></i> <a href="https://github.com/fluid-dev/hexo-theme-fluid" target="_blank" rel="nofollow noopener"><span>Fluid</span></a> 
    </div>
  
  
    <div class="statistics">
  
  

  
    
      <span id="leancloud-site-pv-container" style="display: none">
        总访问量
        <span id="leancloud-site-pv"></span>
        次
      </span>
    
    
      <span id="leancloud-site-uv-container" style="display: none">
        总访客数
        <span id="leancloud-site-uv"></span>
        人
      </span>
    
    

  
</div>

  
  
  
</div>

  </footer>

  <!-- Scripts -->
  
  <script  src="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.js" ></script>
  <link  rel="stylesheet" href="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.css" />

  <script>
    NProgress.configure({"showSpinner":false,"trickleSpeed":100})
    NProgress.start()
    window.addEventListener('load', function() {
      NProgress.done();
    })
  </script>


<script  src="https://lib.baomitu.com/jquery/3.6.4/jquery.min.js" ></script>
<script  src="https://lib.baomitu.com/twitter-bootstrap/4.6.1/js/bootstrap.min.js" ></script>
<script  src="/js/events.js" ></script>
<script  src="/js/plugins.js" ></script>


  <script  src="https://lib.baomitu.com/typed.js/2.0.12/typed.min.js" ></script>
  <script>
    (function (window, document) {
      var typing = Fluid.plugins.typing;
      var subtitle = document.getElementById('subtitle');
      if (!subtitle || !typing) {
        return;
      }
      var text = subtitle.getAttribute('data-typed-text');
      
        typing(text);
      
    })(window, document);
  </script>




  
    <script  src="/js/img-lazyload.js" ></script>
  




  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/tocbot/4.20.1/tocbot.min.js', function() {
    var toc = jQuery('#toc');
    if (toc.length === 0 || !window.tocbot) { return; }
    var boardCtn = jQuery('#board-ctn');
    var boardTop = boardCtn.offset().top;

    window.tocbot.init(Object.assign({
      tocSelector     : '#toc-body',
      contentSelector : '.markdown-body',
      linkClass       : 'tocbot-link',
      activeLinkClass : 'tocbot-active-link',
      listClass       : 'tocbot-list',
      isCollapsedClass: 'tocbot-is-collapsed',
      collapsibleClass: 'tocbot-is-collapsible',
      scrollSmooth    : true,
      includeTitleTags: true,
      headingsOffset  : -boardTop,
    }, CONFIG.toc));
    if (toc.find('.toc-list-item').length > 0) {
      toc.css('visibility', 'visible');
    }

    Fluid.events.registerRefreshCallback(function() {
      if ('tocbot' in window) {
        tocbot.refresh();
        var toc = jQuery('#toc');
        if (toc.length === 0 || !tocbot) {
          return;
        }
        if (toc.find('.toc-list-item').length > 0) {
          toc.css('visibility', 'visible');
        }
      }
    });
  });
</script>


  <script src=https://lib.baomitu.com/clipboard.js/2.0.11/clipboard.min.js></script>

  <script>Fluid.plugins.codeWidget();</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/anchor-js/4.3.1/anchor.min.js', function() {
    window.anchors.options = {
      placement: CONFIG.anchorjs.placement,
      visible  : CONFIG.anchorjs.visible
    };
    if (CONFIG.anchorjs.icon) {
      window.anchors.options.icon = CONFIG.anchorjs.icon;
    }
    var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
    var res = [];
    for (var item of el) {
      res.push('.markdown-body > ' + item.trim());
    }
    if (CONFIG.anchorjs.placement === 'left') {
      window.anchors.options.class = 'anchorjs-link-left';
    }
    window.anchors.add(res.join(', '));

    Fluid.events.registerRefreshCallback(function() {
      if ('anchors' in window) {
        anchors.removeAll();
        var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
        var res = [];
        for (var item of el) {
          res.push('.markdown-body > ' + item.trim());
        }
        if (CONFIG.anchorjs.placement === 'left') {
          anchors.options.class = 'anchorjs-link-left';
        }
        anchors.add(res.join(', '));
      }
    });
  });
</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.js', function() {
    Fluid.plugins.fancyBox();
  });
</script>


  <script>Fluid.plugins.imageCaption();</script>

  <script defer src="/js/leancloud.js" ></script>

  <script  src="/js/local-search.js" ></script>





<!-- 主题的启动项，将它保持在最底部 -->
<!-- the boot of the theme, keep it at the bottom -->
<script  src="/js/boot.js" ></script>


  

  <noscript>
    <div class="noscript-warning">博客在允许 JavaScript 运行的环境下浏览效果更佳</div>
  </noscript>
</body>
</html>
